package com.blaze.framework.core.util.exception;

import com.blaze.framework.core.util.domain.HttpErrorMessage;
import com.blaze.framework.core.util.json.JsonUtil;
import org.springframework.http.HttpStatus;
import org.springframework.validation.BindException;
import org.springframework.validation.FieldError;
import org.springframework.web.bind.MethodArgumentNotValidException;

import javax.validation.ConstraintViolation;
import javax.validation.ConstraintViolationException;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.util.Collections;
import java.util.Map;
import java.util.Optional;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.Function;
import java.util.stream.Collectors;

import static org.springframework.http.HttpStatus.BAD_REQUEST;

/**
 * 统一异常处理器注册中心
 */
public class ExceptionHandlerRegistry {
    private ExceptionHandlerRegistry() {

    }

    /**
     * 检查异常集合
     */
    private static final Map<Class<? extends Exception>, Function<Exception, HttpErrorMessage>> CHECK_EXCEPTION_MAP =
            new ConcurrentHashMap<>(32);
    /**
     * 警告异常集合
     */
    private static final Map<Class<? extends Exception>, Function<Exception, HttpErrorMessage>> WARNING_EXCEPTION_MAP =
            new ConcurrentHashMap<>(32);

    static {
        /*初始化异常处理器*/
        initDefaultHandlers();
    }


    /**
     * 转换错误信息
     * @param ex ex
     * @return HttpErrorMessage
     */
    public static HttpErrorMessage findError(Exception ex) {
        return findHandler(ex)
                .map(handler -> handler.apply(ex))
                .orElseGet(() -> defaultExceptionHandler(ex));
    }

    /**
     * 注册检查异常
     *
     * @param exceptionType 异常类型
     * @param handler       处理器
     * @param <T>           T
     */
    @SuppressWarnings("unchecked")
    private static <T extends Exception> void registerCheckException(Class<T> exceptionType,
                                                                     Function<T, HttpErrorMessage> handler) {
        CHECK_EXCEPTION_MAP.put(exceptionType, (Function<Exception, HttpErrorMessage>) handler);
    }

    /**
     * 注册警告异常
     *
     * @param exceptionType 异常类型
     * @param handler       处理器
     * @param <T>           T
     */
    @SuppressWarnings("unchecked")
    private static <T extends Exception> void registerWarningException(Class<T> exceptionType,
                                                                       Function<T, HttpErrorMessage> handler) {
        WARNING_EXCEPTION_MAP.put(exceptionType, (Function<Exception, HttpErrorMessage>) handler);
    }

    /**
     * 查询handler
     *
     * @param exception exception
     * @return optional function
     */
    private static Optional<Function<Exception, HttpErrorMessage>> findHandler(Exception exception) {
        Class<?> exceptionClass = exception.getClass();
        // 从检查异常中查询
        Function<Exception, HttpErrorMessage> handler = findInHierarchy(exceptionClass, CHECK_EXCEPTION_MAP);
        if (handler != null) {
            return Optional.of(handler);
        }
        // 从警告异常中查询
        handler = findInHierarchy(exceptionClass, WARNING_EXCEPTION_MAP);
        return Optional.ofNullable(handler);
    }


    /**
     * 在层级中查找符合条件的异常方法
     *
     * @param exceptionClass exceptionClass
     * @param map            map
     * @return function
     */
    private static Function<Exception, HttpErrorMessage> findInHierarchy(Class<?> exceptionClass,
                                                                         Map<Class<? extends Exception>,
                                                                                 Function<Exception,
                                                                                         HttpErrorMessage>> map) {

        Class<?> current = exceptionClass;
        while (Exception.class.isAssignableFrom(current)) {
            Function<Exception, HttpErrorMessage> handler = map.get(current);
            if (handler != null) {
                return handler;
            }
            current = current.getSuperclass();
        }
        return null;
    }

    /**
     * 默认的异常处理器
     *
     * @param ex ex
     * @return HttpErrorMessage
     */
    private static HttpErrorMessage defaultExceptionHandler(Exception ex) {
        return new HttpErrorMessage(
                HttpStatus.INTERNAL_SERVER_ERROR.value(),
                "Internal server error",
                System.currentTimeMillis(),
                extractStacktrace(ex)
        );
    }

    /**
     * 提取异常详情
     *
     * @param ex ex
     * @return 异常详情
     */
    public static String extractErrorDetails(Exception ex) {
        if (ex instanceof ConstraintViolationException) {
            return ((ConstraintViolationException) ex).getConstraintViolations().stream()
                    .map(ExceptionHandlerRegistry::formatViolation)
                    .collect(Collectors.joining("; "));
        } else if (ex instanceof MethodArgumentNotValidException) {
            return ((MethodArgumentNotValidException) ex).getBindingResult().getFieldErrors().stream()
                    .map(ExceptionHandlerRegistry::formatFieldError)
                    .collect(Collectors.joining("; "));
        } else if (ex instanceof BindException) {
            return ((BindException) ex).getBindingResult().getFieldErrors().stream()
                    .map(ExceptionHandlerRegistry::formatFieldError)
                    .collect(Collectors.joining("; "));
        }
        return JsonUtil.toJsonString(Collections.singletonMap("error", ex.getMessage()));
    }

    /**
     * 提取堆栈信息
     *
     * @param ex ex
     * @return 堆栈信息
     */
    public static String extractStacktrace(Exception ex) {
        StringWriter sw = new StringWriter();
        ex.printStackTrace(new PrintWriter(sw));
        return sw.toString();
    }

    /**
     * 初始化默认异常处理器
     */
    private static void initDefaultHandlers() {
        registerCheckException(ConstraintViolationException.class,
                ex -> HttpErrorMessage.of(BAD_REQUEST.value(), extractErrorDetails(ex)));

        registerCheckException(BindException.class,
                ex -> HttpErrorMessage.of(BAD_REQUEST.value(), extractErrorDetails(ex)));

        registerWarningException(BusinessException.class,
                ex -> HttpErrorMessage.of(BAD_REQUEST.value(), extractErrorDetails(ex)));

        registerWarningException(ServiceException.class,
                ex -> HttpErrorMessage.of(BAD_REQUEST.value(), extractErrorDetails(ex)));
    }

    private static String formatViolation(ConstraintViolation<?> violation) {
        return String.format("%s %s (实际值: %s)",
                violation.getPropertyPath(),
                violation.getMessage(),
                violation.getInvalidValue());
    }

    private static String formatFieldError(FieldError error) {
        return String.format("%s %s (实际值: %s)",
                error.getField(),
                error.getDefaultMessage(),
                error.getRejectedValue());
    }
}
